package com.xunshi.hikangvision.untils;

import com.sun.jna.Pointer;
import com.sun.jna.ptr.ByteByReference;
import com.xunshi.hikangvision.vo.MyBlockingQueue;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.BlockingQueue;
import static com.xunshi.hikangvision.untils.PreverViewUntil.hCNetSDK;

public class VidePreView {

    static FRealDataCallBack1 fRealDataCallBack;//预览回调函数实现
    static VideoDemo.fPlayEScallback fPlayescallback; //裸码流回调函数
    public static int lPlay = -1;  //预览句柄
    public static boolean lPlayStatus = false;//预览是否成功
    public static String lPlayErrorMassage = "";//预览错误信息


    /**
     * 预览摄像头
     * @param userID 登录时返回的id
     * @param iChannelNo 通过哪个通道预览
     */
    public static void RealPlay(int userID, int iChannelNo) {
        if (userID == -1) {
            System.out.println("请先登录");
            lPlayStatus=false;
            lPlayErrorMassage="请先登录";
            return;
        }
        HCNetSDK.NET_DVR_PREVIEWINFO strClientInfo = new HCNetSDK.NET_DVR_PREVIEWINFO();
        strClientInfo.read();
        strClientInfo.hPlayWnd = null;  //窗口句柄，从回调取流不显示一般设置为空
        strClientInfo.lChannel = iChannelNo;  //通道号
        strClientInfo.dwStreamType=0; //0-主码流，1-子码流，2-三码流，3-虚拟码流，以此类推
        strClientInfo.dwLinkMode=0; //连接方式：0- TCP方式，1- UDP方式，2- 多播方式，3- RTP方式，4- RTP/RTSP，5- RTP/HTTP，6- HRUDP（可靠传输） ，7- RTSP/HTTPS，8- NPQ
        strClientInfo.bBlocked=1;
        strClientInfo.write();

        //回调函数定义必须是全局的
        if (fRealDataCallBack == null) {
            fRealDataCallBack = new FRealDataCallBack1();
        }

        //开启预览
        lPlay = hCNetSDK.NET_DVR_RealPlay_V40(userID, strClientInfo, fRealDataCallBack , null);
        if (lPlay == -1) {
            int iErr = hCNetSDK.NET_DVR_GetLastError();
            System.out.println("取流失败" + iErr);
            lPlayStatus=false;
            lPlayErrorMassage="取流失败,错误码：" + iErr;
            return;
        }
        System.out.println("取流成功");
        lPlayStatus=true;
        return;
    }

    //预览回调
    static class FRealDataCallBack1 implements HCNetSDK.FRealDataCallBack_V30 {
        //预览回调   lRealHandle预览句柄回调
        public void invoke(int lRealHandle, int dwDataType, ByteByReference pBuffer, int dwBufSize, Pointer pUser) {
            //System.out.println("码流数据回调, 数据类型: " + dwDataType + ", 数据长度:" + dwBufSize);
            //播放库解码
            switch (dwDataType) {
                case HCNetSDK.NET_DVR_SYSHEAD: //系统头
                case HCNetSDK.NET_DVR_STREAMDATA:   //码流数据
                    if ((dwBufSize > 0)) {
                        //System.out.println("取流回调进行中.....");
                        byte[] outputData = pBuffer.getPointer().getByteArray(0, dwBufSize);
                        try {
                            writeESH264(outputData,lRealHandle);//将流写入对应的实体
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
            }
        }


        //多路视频的pes数据进行缓存，知道某一路视频的RTP包开头进入时进行取出返给前端
        Map<String,byte[]> EsBytesMap=new HashMap<>();
        /**
         * 提取H264的裸流写入文件
         * @param outputData
         * @throws IOException
         */
        public void writeESH264(final byte[] outputData,int lRealHandle) throws IOException {
            if (outputData.length <= 0) {
                return;
            }
            String playIdStr = String.valueOf(lRealHandle);
            byte[] allEsBytes = null;//当前这个通道的一个Rtp包数据
            if(!EsBytesMap.containsKey(playIdStr))
            {
                EsBytesMap.put(playIdStr,allEsBytes);
            }
            else
            {
                allEsBytes=EsBytesMap.get(playIdStr);
            }

            if ((outputData[0] & 0xff) == 0x00//
                    && (outputData[1] & 0xff) == 0x00//
                    && (outputData[2] & 0xff) == 0x01//
                    && (outputData[3] & 0xff) == 0xBA) {// RTP包开头
                // 一个完整的帧解析完成后将解析的数据放入BlockingQueue,websocket获取后发送给前端
                if (allEsBytes != null && allEsBytes.length > 0) {
                    //System.out.println("回调的lRealHandle："+lRealHandle);
                    if(MyBlockingQueue.PlayToUserIdMap.containsKey(String.valueOf(lRealHandle)))
                    {
                        String userId = MyBlockingQueue.PlayToUserIdMap.get(String.valueOf(lRealHandle));
                        BlockingQueue blockingQueue = MyBlockingQueue.bpMap.get(userId);
                        //System.out.println("当前的lPlayID："+lRealHandle);
                        //System.out.println("myBlockingQueue.bq is null?"+(null==blockingQueue));
                        try {
                            blockingQueue.put(allEsBytes);//将当前的某一路视频通道的上一个Rtp包放到队列中去
                            MyBlockingQueue.bpMap.put(userId,blockingQueue);
                            allEsBytes = null;
                            EsBytesMap.put(playIdStr,allEsBytes);//置空当前通道的RTP包，下次回调就是pes包进行取流追加
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
            // 是00 00 01 eo开头的就是视频的pes包
            if ((outputData[0] & 0xff) == 0x00//
                    && (outputData[1] & 0xff) == 0x00//
                    && (outputData[2] & 0xff) == 0x01//
                    && (outputData[3] & 0xff) == 0xE0) {//
                // 去掉包头后的起始位置
                int from = 9 + outputData[8] & 0xff;
                int len = outputData.length - 9 - (outputData[8] & 0xff);
                // 获取es裸流
                byte[] esBytes = new byte[len];
                System.arraycopy(outputData, from, esBytes, 0, len);

                if (allEsBytes == null) {
                    allEsBytes = esBytes;
                } else {
                    byte[] newEsBytes = new byte[allEsBytes.length + esBytes.length];
                    System.arraycopy(allEsBytes, 0, newEsBytes, 0, allEsBytes.length);
                    System.arraycopy(esBytes, 0, newEsBytes, allEsBytes.length, esBytes.length);
                    allEsBytes = newEsBytes;
                }
                EsBytesMap.put(playIdStr,allEsBytes);//当前视频通道的部分包数据进行缓存
            }
        }
    }



}
